import * as fs from 'fs'
import * as path from 'path'
import { execSync } from 'child_process'

export type LooseObject = Record<string, any>

export interface ListConfig {
  language: string
  filename: string
  generator: any
  options?: any
  url?: string
}

interface RegisterListOptions {
  language: string
  filename: string
  url?: string
  generator: any
  options?: LooseObject
}

export default class ListHandler {
  lists: ListConfig[] = []

  languages = new Set<string>()

  async generateData() {
    // eslint-disable-next-line no-restricted-syntax
    for (const options of this.lists) {
      console.info(
        `----------- Starting ${options.language} ${options.filename} -----------`,
      )
      // eslint-disable-next-line no-console
      console.time(options.filename)
      // eslint-disable-next-line new-cap
      const generator = new options.generator({
        options: options.options,
        url: options.url,
      })
      const folder = path.join(
        __dirname,
        '../../packages/languages/',
        options.language,
        'src',
      )
      if (!fs.existsSync(folder)) {
        fs.mkdirSync(folder, { recursive: true })
      }

      // eslint-disable-next-line no-await-in-loop
      const data = await generator.run()
      if (data) {
        fs.writeFileSync(
          path.join(folder, `${options.filename}.json`),
          JSON.stringify(data),
        )
      }
      // eslint-disable-next-line no-console
      console.timeEnd(options.filename)
      console.info(
        `----------- Finished ${options.language} ${options.filename} -----------`,
      )
      this.languages.add(options.language)
    }
  }

  generateIndices() {
    const dataFolder = path.join(__dirname, '../../packages/languages/')

    const languages = fs
      .readdirSync(dataFolder)
      .filter((language) => this.languages.has(language))

    // eslint-disable-next-line max-statements
    languages.forEach((language) => {
      const isCommon = language === 'common'
      const languageFolder = path.join(dataFolder, language, 'src')
      const files = fs
        .readdirSync(languageFolder)
        .filter((file) => file.endsWith('.json'))

      if (!isCommon) {
        files.push('translations')
      }

      const indexPath = path.join(languageFolder, 'index.ts')
      const imports = files
        .map((file) => `import ${file.replace('.json', '')} from "./${file}"`)
        .join('\n')

      const hasAdjacencyGraphs = files.includes('adjacencyGraphs.json')

      const filesToIgnore = ['translations', 'adjacencyGraphs.json']
      const dictionaryExports = files
        .filter((file) => {
          return !filesToIgnore.includes(file)
        })
        .map((file) => {
          const dictionaryName = file.replace('.json', '')
          return `'${dictionaryName}-${language}': ${dictionaryName}`
        })
        .join(',\n  ')

      const translations = isCommon ? '' : 'translations,'
      const adjacencyGraphs = hasAdjacencyGraphs ? 'adjacencyGraphs,' : ''

      fs.writeFileSync(
        indexPath,
        `// This file is auto generated by data-scripts/_helpers/runtime.ts
${imports}

const dictionary = { ${dictionaryExports} }

export {
    dictionary,
    ${translations}
    ${adjacencyGraphs}
}`,
      )
      try {
        execSync(`eslint --ext .ts --fix --cache ${indexPath}`)
      } catch (error: any) {
        if (error.stdout) {
          console.error((error.stdout as Buffer).toString('utf8'))
          throw new Error(`Eslint failed for file ${indexPath}`)
        }
        throw error
      }
    })
  }

  async run() {
    await this.generateData()
    this.generateIndices()
  }

  registerList(options: RegisterListOptions) {
    this.lists.push(options)
  }
}
